﻿/* i2c_comm.c */

#include "i2c_comm.h"

void i2c_slave_xfer_done_hook(void);

/* 配置master轮询方式. */
void i2c_init_master(I2C_Type * base, i2c_speed_t speed)
{
    CLOCK_EnableClock(kCLOCK_I2c0);

    /* set bit rate. */
    base->CLKDIV = I2C_CLKDIV_DIVVAL(0x1); /* 原始时钟源的2分频作为i2c_clk. */
    base->MSTTIME = I2C_MSTTIME_MSTSCLLOW(0x7) | I2C_MSTTIME_MSTSCLHIGH(0x7); /* SCL基于i2c_clk的波形. */

    /* master function. */
    base->CFG = I2C_CFG_MSTEN_MASK; /* enable master. */
}

/* bus上的读, 不是协议的读. */
void i2c_master_read(I2C_Type * base, uint8_t dev_addr, uint8_t *buff, uint8_t len)
{
     /* start with target address, read. */
    base->MSTDAT = I2C_DEV_ADDR(dev_addr) | I2C_DEV_ADDR_READ_MASK; /* read. */
    base->MSTCTL = I2C_MSTCTL_MSTSTART_MASK;
    /* wait while the i2c master is sending the slave address. */
    while ( ( base->STAT & I2C_STAT_MSTPENDING_MASK) == 0 )
    {}

    /* read data from slave. */
    while ( len > 1)
    {
        /* wait while the i2c master is sending the slave address. */
        while ( ( base->STAT & I2C_STAT_MSTPENDING_MASK) == 0 )
        {}
        *buff++ = (uint8_t)(base->MSTDAT);
        base->MSTCTL = I2C_MSTCTL_MSTCONTINUE_MASK; /* send ack to slave for next byte. */
        len--;
    }

    /* read the final byte ending with nack and stop */
    while ( ( base->STAT & I2C_STAT_MSTPENDING_MASK) == 0 )
    {}
    *buff = (uint8_t)(base->MSTDAT);
    base->MSTCTL = I2C_MSTCTL_MSTSTOP_MASK; /* send stop with nack. */
}

void i2c_master_write(I2C_Type * base, uint8_t dev_addr, uint8_t *buff, uint8_t len)
{
     /* start with target address. */
    base->MSTDAT = I2C_DEV_ADDR(dev_addr) | I2C_DEV_ADDR_WRITE_MASK; /* write. */
    base->MSTCTL = I2C_MSTCTL_MSTSTART_MASK;
    /* wait while the i2c master is sending the slave address. */
    while ( ( base->STAT & I2C_STAT_MSTPENDING_MASK) == 0 )
    {}

    /* send data context. */
    for (uint8_t i = 0u; i < len; i++)
    {
        base->MSTDAT = buff[i];
        base->MSTCTL = I2C_MSTCTL_MSTCONTINUE_MASK;
        /* wait while the i2c master is sending data. */
        while ( ( base->STAT & I2C_STAT_MSTPENDING_MASK) == 0 )
        {}
    }

    /* send stop. */
    base->MSTCTL = I2C_MSTCTL_MSTSTOP_MASK;
}

/*******************************************************************************
 * i2c slave.
 *******************************************************************************/
volatile uint8_t *i2c_slave_data_buf = NULL;
volatile uint8_t i2c_slave_data_buff_len = 0u;
volatile uint8_t i2c_slave_data_idx = 0u;

volatile i2c_func1_t i2c_slave_xfer_done_callback_func;
volatile i2c_slave_xfer_done_callback_param_t i2c_slave_xfer_done_callback_param_struct;

void i2c_slave_install_xfer_done_callback(I2C_Type *base, i2c_func1_t func)
{
    if (base == I2C0)
    {
        i2c_slave_xfer_done_callback_func = func;
    }
}

void i2c_init_slave(I2C_Type * base, uint8_t slave_addr, uint8_t *data_buf, uint8_t data_len)
{
    CLOCK_EnableClock(kCLOCK_I2c0);

    i2c_slave_data_buf = data_buf;
    i2c_slave_data_buff_len = data_len;
    i2c_slave_data_idx = 0u;

    base->CFG = I2C_CFG_SLVEN_MASK; /* enable slave mode. */

    base->SLVADR[0] = I2C_DEV_ADDR(slave_addr); /* set slave addr and enable it. */

    i2c_slave_xfer_done_callback_func = NULL;

    /* enable interrupt. */
    //base->INTSTAT = I2C_INTSTAT_SLVPENDING_MASK | I2C_INTSTAT_SLVDESEL_MASK; /* clear flags. */
    base->INTENSET = I2C_INTENSET_SLVPENDINGEN_MASK | I2C_INTENSET_SLVDESELEN_MASK;
    NVIC_EnableIRQ(I2C0_IRQn);
}

void i2c_disable_slave(I2C_Type *base)
{
    base->CFG &= ~I2C_CFG_SLVEN_MASK;
}

void i2c_enable_slave(I2C_Type *base)
{
    base->CFG |= I2C_CFG_SLVEN_MASK;
}

/* 在应用层由i2c中断程序调用. */
volatile uint8_t i2c_slave_addr; /* the dummy read for address period. */
volatile uint32_t i2c_slave_xfer_done_flags;
volatile bool i2c_slave_flag_first_receive_after_address = false;
volatile uint8_t i2c_slave_rx_cmd; /* first item of rx frame. */

void i2c_slave_isr_hook(I2C_Type *base)
{
    uint32_t flags = base->STAT;

    if ( 0u != (flags & I2C_STAT_SLVDESEL_MASK) ) /* stop comes. */
    {
        base->STAT = I2C_STAT_SLVDESEL_MASK; /* clear flag. */

        /* execute the STOP callback. */
        if (i2c_slave_xfer_done_callback_func)
        {
            i2c_slave_xfer_done_callback_param_struct.rx_cmd = i2c_slave_rx_cmd;
            i2c_slave_xfer_done_callback_param_struct.flags = i2c_slave_xfer_done_flags; /* clear the flags. */
            i2c_slave_xfer_done_callback_param_struct.xfer_len = i2c_slave_data_idx;
            i2c_slave_xfer_done_callback_param_struct.xfer_data = (uint8_t *)i2c_slave_data_buf;
            i2c_slave_xfer_done_callback_func((void *)&i2c_slave_xfer_done_callback_param_struct);
        }
    }
    else if (0u != (flags & I2C_STAT_SLVPENDING_MASK) )
    {
        uint32_t _i2c_slave_state = (flags & I2C_STAT_SLVSTATE_MASK)>>I2C_STAT_SLVSTATE_SHIFT;

        switch (_i2c_slave_state)
        {
        case 0u: /* Slave address. Address plus R/W received. At least  one of the four slave addresses has been matched by hardware. */
            /* check which slave address (1 in 4) is matched. read address from slvdata reg, or check the flags. */
            i2c_slave_addr= (uint8_t)(base->SLVDAT);

            /* initialize the flags and counters. */
            i2c_slave_data_idx = 0u; /* always start from 0. */
            i2c_slave_flag_first_receive_after_address = true; /* the first item of receive frame is empty. */
            i2c_slave_rx_cmd = 0u;
            i2c_slave_xfer_done_flags = 0;
            memset((void *)&i2c_slave_xfer_done_callback_param_struct, 0, sizeof(i2c_slave_xfer_done_callback_param_t));

            break;

        case 1u: /*  Slave receive. Received data is available (Slave Receiver mode).*/
            /* set the rx flag. */
            if (0u == ( i2c_slave_xfer_done_flags & I2C_SLAVE_XFER_DONE_FLAG_RECEIVE_DATA) )
            {
                i2c_slave_xfer_done_flags |= I2C_SLAVE_XFER_DONE_FLAG_RECEIVE_DATA;
            }

            if (i2c_slave_flag_first_receive_after_address)
            {
                i2c_slave_rx_cmd = (uint8_t)(base->SLVDAT); /* skip the first cmd word. */
                i2c_slave_flag_first_receive_after_address = false; /* one time clear. */
            }
            else
            {
                if (i2c_slave_data_idx < i2c_slave_data_buff_len)
                {
                     i2c_slave_data_buf[i2c_slave_data_idx++] = (uint8_t)(base->SLVDAT);
                }
                else
                {
                    volatile uint8_t _dummy = (uint8_t)(base->SLVDAT);
                    //_i2c_slave_err = true; /* too much data from master. */
                    i2c_slave_xfer_done_flags |= I2C_SLAVE_XFER_DONE_FLAG_RX_OVERFLOW;
                }
            }
            break;

        case 2u: /* Slave transmit. Data can be transmitted (Slave Transmitter mode). */
            /* set the tx flag. */
            if (0u == ( i2c_slave_xfer_done_flags & I2C_SLAVE_XFER_DONE_FLAG_TRANSMIT_DATA) )
            {
                i2c_slave_xfer_done_flags |= I2C_SLAVE_XFER_DONE_FLAG_TRANSMIT_DATA;
            }

            if (i2c_slave_data_idx <i2c_slave_data_buff_len)
            {
                 base->SLVDAT = i2c_slave_data_buf[i2c_slave_data_idx++];
            }
            else
            {
                base->SLVDAT = 0x0;
                //_i2c_slave_err = true; /* no more data  for master. */
                i2c_slave_xfer_done_flags |= I2C_SLAVE_XFER_DONE_FLAG_TX_OVERFLOW;
            }
            break;

        default:
            break;
        }

        if ( 0u != (i2c_slave_xfer_done_flags & (  I2C_SLAVE_XFER_DONE_FLAG_RX_OVERFLOW
                                            | I2C_SLAVE_XFER_DONE_FLAG_TX_OVERFLOW) ) )
        {
            base->SLVCTL = I2C_SLVCTL_SLVNACK_MASK;
        }
        else
        {
            base->SLVCTL = I2C_SLVCTL_SLVCONTINUE_MASK;
        }
    }
}

/* hardware entry for i2c0 isr. */
void I2C0_IRQHandler(void)
{
    i2c_slave_isr_hook(I2C0);
}

/* EOF. */

